﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace xCarLinkMP3Organizer
{
    class UsbDriveStateMonitor : NativeWindow, IDisposable
    {
        public UsbDriveStateMonitor()
        {
            base.CreateHandle(new CreateParams());
        }

        public void Dispose()
        {
            base.DestroyHandle();
            GC.SuppressFinalize(this);
        }

        public event EventHandler<UsbStateChangedEventArgs> StateChanged;
        
        protected override void WndProc(ref Message message)
        {
            base.WndProc(ref message);

            if ((message.Msg == Win32.WM_DEVICECHANGE) && (message.LParam != IntPtr.Zero))
            {
                var volume = (Win32.DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(
                    message.LParam, typeof(Win32.DEV_BROADCAST_VOLUME));

                if (volume.dbcv_devicetype == Win32.DBT_DEVTYP_VOLUME)
                {
                    switch (message.WParam.ToInt32())
                    {
                        case Win32.DBT_DEVICEARRIVAL:
                            RaiseStateChanged(UsbStateChange.Added, volume);
                            break;

                        case Win32.DBT_DEVICEREMOVECOMPLETE:
                            RaiseStateChanged(UsbStateChange.Removed, volume);
                            break;
                    }
                }
            }
        }
        
        private void RaiseStateChanged(UsbStateChange state, Win32.DEV_BROADCAST_VOLUME volume)
        {
            if (StateChanged != null)
            {
                string name = ToUnitName(volume.dbcv_unitmask);
                StateChanged(this, new UsbStateChangedEventArgs(state, name));
            }
        }

        private static string ToUnitName(int mask)
        {
            int offset = 0;
            while ((offset < 26) && ((mask & 0x00000001) == 0))
            {
                mask = mask >> 1;
                offset++;
            }

            if (offset < 26)
            {
                return String.Format("{0}:", Convert.ToChar(Convert.ToInt32('A') + offset));
            }

            return "?:";
        }
    }

    class UsbStateChangedEventArgs : EventArgs
    {
        public UsbStateChangedEventArgs(UsbStateChange state, string name)
        {
            State = state;
            Name = name;
        }

        public string Name
        {
            get;
            private set;
        }

        public UsbStateChange State
        {
            get;
            private set;
        }
    }

    public enum UsbStateChange
    {
        Added,
        Removed
    }

    class Win32
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct DEV_BROADCAST_VOLUME
        {
            public int dbcv_size;			// size of the struct
            public int dbcv_devicetype;		// DBT_DEVTYP_VOLUME
            public int dbcv_reserved;		// reserved; do not use
            public int dbcv_unitmask;		// Bit 0=A, bit 1=B, and so on (bitmask)
            public short dbcv_flags;		// DBTF_MEDIA=0x01, DBTF_NET=0x02 (bitmask)
        }

        public const int WM_DEVICECHANGE = 0x0219;				// device state change
        public const int DBT_DEVICEARRIVAL = 0x8000;			// detected a new device
        public const int DBT_DEVICEREMOVECOMPLETE = 0x8004;	// removed 
        public const int DBT_DEVTYP_VOLUME = 0x00000002;		// logical volume
    }
}
